/*
 * Options.java
 *
 * Created on 10/Dez/2011, 18:04:24
 */

package intopo.evogame.partnerselection.tools.config;

import intopo.game.DescriptionGUI;
import java.awt.Color;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.NoSuchElementException;
import java.util.Scanner;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 * Generic options of the config file editor.
 *
 *
 * @author <a href="mailto:mariano.pedro@gmail.com">Pedro Mariano</a>
 *
 * @version 1.0 10/Dez/2011
 */
public class Options
{
	static Color ok = Color.BLACK;
	static Color error = Color.RED;
	/**
	 * Games to use in the config file editor.
	 */
	Vector<GameData> gameData;
	/**
	 * Last location of the config file that is displayed in the GUI.  This is the string that is used to initialise the text field.
	 */
	String defaultConfigFilename;
	Options ()
	{
		this.gameData = new Vector<GameData> ();
		load ();
	}
	String[] comboBoxModel ()
	{
		String[] result = new String [gameData.size ()];
		int i = 0;
		for (GameData gd : this.gameData) {
			result [i++] = gd.name;
		}
		return result;
	}
	Vector<GameData> getGameData ()
	{
		return this.gameData;
	}

	void save ()
	{
		PrintStream ps;
		try {
			String optionsFilename = System.getProperty ("user.home") + System.getProperty ("file.separator") + "config-evogame-partnerselection";
			ps = new PrintStream (optionsFilename);
		}
		catch (SecurityException e) {
			return ;
		}
		catch (FileNotFoundException e) {
			return ;
		}
		ps.println (this.defaultConfigFilename);
		for (GameData gd : this.gameData) {
			gd.write (ps);
		}
		ps.close ();
	}
	/**
	 * Loads the user options files in order to initialise the fields of this class.  This method is only called in the constructor.
	 */
	private void load ()
	{
		String errorMessages = null;
		Scanner scanner = null;
		try {
			String optionsFilename = System.getProperty ("user.home") + System.getProperty ("file.separator") + "config-evogame-partnerselection";
			scanner = new Scanner (new FileInputStream (optionsFilename));
		}
		catch (SecurityException e) {
			defaultOptions ();
			return ;
		}
		catch (FileNotFoundException e) {
			defaultOptions ();
			return ;
		}
		if (scanner.hasNextLine ()) {
			this.defaultConfigFilename = scanner.nextLine ();
			while (scanner.hasNext ()) {
				try {
					this.gameData.add (new GameData (scanner));
				}
				catch (GameDataException ex) {
					if (errorMessages == null) {
						errorMessages = ex.getMessage ();
					}
					else {
						errorMessages += "\n" + ex.getMessage ();
					}
				}
			}
			if (gameData.size () == 0) {
				defaultGameOptions ();
			}
		}
		else {
			defaultOptions ();
		}
		scanner.close ();
		if (errorMessages != null) {
			JOptionPane.showMessageDialog (null, errorMessages, "Error", JOptionPane.ERROR_MESSAGE);
		}
	}
	private void defaultOptions ()
	{
		this.defaultConfigFilename = "config.txt";
		this.defaultGameOptions ();
	}
	private void defaultGameOptions ()
	{
		this.gameData.add (new GameData ("Give-Take", intopo.game.impl.givetake.GiveTakeDescriptionTextGUI.getInstance ()));
	}
	static class GameData
	{
		/**
		 * String to be used in the game combo box.
		 */
		final String name;
		/**
		 * Game description.  Used to obtain the component to edit the game and its strategies.
		 */
		final DescriptionGUI description;
		private GameData (String name, DescriptionGUI description)
		{
			this.name = name;
			this.description = description;
		}
		private GameData (Scanner scanner)
			throws GameDataException
		{
			String className;
			try {
				this.name = scanner.nextLine ();
				className = scanner.nextLine ();
			}
			catch (NoSuchElementException ex) {
				throw new GameDataException ("Options file is corrupted", ex);
			}
			Class c = null;
			try {
				c = Class.forName (className);
			}
			catch (ClassNotFoundException e1) {
				try {
					ClassLoader cl = ClassLoader.getSystemClassLoader ();
					c = cl.loadClass (className);
				}
				catch (ClassNotFoundException e2) {
					throw new GameDataException ("Could not load class " + className, e2);
				}
				catch (SecurityException e2) {
					throw new GameDataException ("Could not load class " + className, e2);
				}
				catch (IllegalStateException e2) {
					throw new GameDataException ("Could not load class " + className, e2);
				}
				catch (Error e2) {
					throw new GameDataException ("Could not load class " + className, e2);
				}
			}
//			boolean ok = false;
//			for (Class i : c.getInterfaces ()) {
//				if (i.getCanonicalName ().equals ("intopo.game.Description")) {
//					ok = true;
//				}
//			}
//			if (!ok) {
//				throw new GameDataException ("Class " + className + " does not implement interface intopo.game.Description");
//			}
			try {
				this.description = (DescriptionGUI) c.newInstance ();
			}
			catch (ClassCastException e) {
				throw new GameDataException ("Class " + className + " does not implement interface intopo.game.DescriptionGUI", e);
			}
			catch (InstantiationException e) {
				throw new GameDataException ("Error instantiating class " + className, e);
			}
			catch (IllegalAccessException e) {
				throw new GameDataException ("Error instantiating class " + className, e);
			}
		}

		private void write (PrintStream ps)
		{
			ps.println (this.name);
			ps.println (this.description.getClass ().getCanonicalName ());
		}
	}
	static private class GameDataException
		extends Exception
	{
		GameDataException (String message)
		{
			super (message);
		}
		GameDataException (String message, Throwable cause)
		{
			super (message, cause);
		}
	}
}

// Local Variables:
// mode: java
// mode: flyspell-prog
// ispell-local-dictionary: "british"
// End:
